/*
 * Created on Oct 21, 2004
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.hermeneute.fsa;

import java.io.IOException;
import java.io.PushbackReader;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;

/**
 * @author mauceri
 *
 */

class ThingComparator implements Comparator {
	public int compare(Object o1, Object o2) {
		return ((Thing) o1).compareTo((Thing) o2);
	}
}

class Thing {
	Object o;

	int size;

	Thing(Object o, int sz) {
		this.o = o;
		size = sz;
	}

	Thing() {
		o = null;
		size = 0;
	}

	void set(Object o, int sz) {
		this.o = o;
		size = sz;
	}

	public int compareTo(Thing t) {
		if (size == t.size) {
			return 0;
		} else if (size < t.size) {
			return 1;
		} else {
			return -1;
		}
	}
}

class Marker {
	public int start;

	public int end;

	public String value;
}

public class FSTSequentialTransducer {
	public int start;

	int currentState = 0;

	public HashSet finalStates;

	HashSet sinkStates;

	FSTSequentialCompiledState stateTable[];

	int inputIdx[];

	int cchar;

	int traceNextState = 0;

	String inputRead;

	int inputPos;

	int markInputPos;

	int markStartPos;

	String eres = "";

	boolean match = false;

	boolean eof = false;

	boolean inmark = false;

	int startMark = 0;

	int endMark = 0;

	Vector memoryMark = new Vector();

	public FSTSequentialTransducer(FSTAutomaton a) {
		this.finalStates = a.finalStates;
		this.sinkStates = a.sinkStates;
		this.start = a.start;
		compile(a);
		a = null;
	}

	void compile(FSTAutomaton a) {
		Vector stateVector = new Vector();
		Vector eventVector = new Vector();
		Hashtable eventTable = new Hashtable();
		int evtnum = 0;
		int maxInput = 0;

		for (Enumeration e = a.getStates(); e.hasMoreElements();) {
			Integer is1 = (Integer) e.nextElement();
			int size = 0;
			for (FSTTransitionIterator it = a.getTransitions(is1); it.hasNext();) {
				it.next();
				Integer es = (Integer) eventTable.get(new Integer(it.getEvent()
						.getIn()));

				if (es == null) {
					evtnum++;
					eventTable.put(new Integer(it.getEvent().getIn()),
							new Integer(1));
				} else {
					eventTable.put(new Integer(it.getEvent().getIn()),
							new Integer(es.intValue() + 1));
				}
				size++;
			}
			stateVector.add(new Thing(is1, size));
		}

		for (Enumeration it = eventTable.keys(); it.hasMoreElements();) {
			Integer evt = (Integer) it.nextElement();
			eventVector.add(new Thing(evt, ((Integer) eventTable.get(evt))
					.intValue()));
		}

		Collections.sort(eventVector, new ThingComparator());
		inputIdx = new int[256 * 256];
		for (int i = 0; i < 256 * 256; i++)
			inputIdx[i] = -1;

		maxInput = 0;
		for (Iterator it = eventVector.iterator(); it.hasNext();) {
			Thing th = (Thing) it.next();
			int evt = ((Integer) th.o).intValue();
			int size = th.size;
			inputIdx[evt] = maxInput++;
			/*String chacha;
			 
			 if (evt == 1) {
			 //System.out.println("Aqui \1");
			 chacha = "\\1";
			 }
			 else if (evt == 2) {
			 //System.out.println("Aqua \1");
			 chacha = "\\2";
			 }
			 else if (evt == ' ') {
			 //System.out.println("Nada ");
			 chacha = "\\b";
			 }
			 else if (evt == '\\') {
			 //System.out.println("Nada ");
			 chacha = "\\";
			 }
			 else
			 chacha = " "+(char)evt;
			 System.out.println("Input: "+chacha+" = "+inputIdx[evt]+" "+size);*/
		}
		stateTable = new FSTSequentialCompiledState[a.firstFreeState];
		for (Iterator it = stateVector.iterator(); it.hasNext();) {
			Thing th = (Thing) it.next();
			int s = ((Integer) th.o).intValue();
			int size = th.size;
			if (size == 1) {
				FSTTransitionIterator trit = a.getTransitions(s);
				trit.next();
				stateTable[s] = new FSTSequentialTinyCompiledState(trit
						.getTransition(), inputIdx);
				if (stateTable[s] == null)
					System.out.println("Ayyeee!!!!");
			} else if (size == 2) {
				FSTTransitionIterator trit = a.getTransitions(s);
				trit.next();
				FSTTransition tr1 = trit.getTransition();
				trit.next();
				FSTTransition tr2 = trit.getTransition();
				stateTable[s] = new FSTSequentialSmallCompiledState(tr1, tr2,
						inputIdx);
				if (stateTable[s] == null)
					System.out.println("Ayyeee!!!!");
			} else if (size < (inputIdx.length * 3) / 4) {
				stateTable[s] = new FSTSequentialMediumCompiledState(a
						.getTransitions(s), inputIdx);
				if (stateTable[s] == null)
					System.out.println("Ayyeee!!!!");
			} else {
				stateTable[s] = new FSTSequentialBigCompiledState(a
						.getTransitions(s), inputIdx);
				System.out.println("Big state");
				if (stateTable[s] == null)
					System.out.println("Ayyeee!!!!");
			}
		}
	}
	
	public FSTCompiledTransition nextEpsilonState(int state) {
		if (stateTable[state] != null) {
			return stateTable[state].nextState(inputIdx[FSTEvent.Epsilon]);
		}else{
			return null;
		}
	}
	public FSTCompiledTransition nextState(int state, int input) {
		FSTCompiledTransition res = null;
		cchar = input;

		//System.out.println("<"+(char)cchar+">");
		if (input == -1) {
			//System.out.println("Here 2 "+(char)input+" "+input);
			return null;
		}

		if (stateTable[state] != null) {
			res = stateTable[state].nextState(inputIdx[input]);
			if (res == null && input != 1) {
				res = stateTable[state].nextState(inputIdx[2]);
			}
			return res;
		} else {
			return null;
		}
	}

	String tryEpsilon() {
		FSTCompiledTransition ctr = null;
		String res = "";
		int i = 0;
		while ((ctr = nextState(currentState, 1)) != null) {
			res += ctr.output;
			currentState = ctr.state;
			if (res.equals("(")) {
				return res;
			}
			if (res.equals(")")) {
				return res;
			}
		}
		if (finalStates.contains(new Integer(currentState))) {
			match = true;
		}
		return res;
	}

	int readNext(PushbackReader in) throws IOException {

		if (in.ready()) {
			int res = in.read();
			if (res == -1) {
				eof = true;
			} else {
				//System.out.print((char)res);
				inputRead += (char) res;
				inputPos++;
			}
			return res;
		} else {
			eof = true;
			return -1;
		}
	}

	boolean tryLeftTag() {
		FSTCompiledTransition ctr = null;
		String res = "";
		int i = 0;
		if ((ctr = nextState(currentState, 1)) != null) {
			if (ctr.output == '(') {
				currentState = ctr.state;
				return true;
			}
		}
		return false;
	}

	boolean tryRightTag() {
		FSTCompiledTransition ctr = null;
		String res = "";
		int i = 0;
		if ((ctr = nextState(currentState, 1)) != null) {
			if (ctr.output == ')') {
				currentState = ctr.state;
				return true;
			}
		}
		return false;
	}

	String evalTodo(String todo, String match) {
		String res = "";
		String command = todo.substring(0, 4);
		String args[];
		todo = todo.substring(5);
		args = todo.split(",");
		//System.out.println(todo);
		int pos = 0;
		for (int i = 0; i < args.length; i++) {
			String arg = args[i];
			Marker m = (Marker) memoryMark.elementAt(i);
			if (command.equals("tag:")) {
				//System.out.println("tag: "+m.value+" "+arg);
				res += match.substring(pos, m.start);
				res += "<" + arg + ">" + m.value + "</" + arg + ">";
				pos = m.end;
			} else if (command.equals("del:")) {
				//System.out.println("del: "+m.value+" "+arg);
				res += match.substring(pos, m.start);
				pos = m.end;
			} else if (command.equals("rep:")) {
				System.out.println("rep: " + m.value + " " + arg
						+ match.substring(pos, m.start) + arg);
				res += match.substring(pos, m.start) + arg;
				pos = m.end;
			} else {
				System.err.println(command + " is an unknown command");
			}
		}
		//res += match.substring(pos);
		return res;
	}

	FSTResult parseMark(PushbackReader in) {
		FSTResult res = new FSTResult();
		String outputs = "";
		String lastOutput = null;
		int cLastPos = 0;
		String lastEres = "";
		int lastRead;
		eres = "";

		FSTCompiledTransition ctr = null;
		FSTCompiledTransition ltr = null;
		match = false;
		cchar = 0;
		inputPos = 0;
		inputRead = "";
		eof = false;
		inmark = false;

		try {
			while (!match && !eof) {
				currentState = start;
				if (tryLeftTag()) {
					if (inmark) {
						System.err.println("Skipping last mark" + inputRead);
					}
					inmark = true;
				}
				while (!eof
						&& (ctr = nextState(currentState, readNext(in))) == null) {
					res.startPos = this.inputPos;
				}
				if (ctr != null && !eof) {
					markInputPos = inputPos;
					markStartPos = inputPos - 1;
					if (inmark) {
						startMark = inputPos - 1;
					}
					currentState = ctr.state;

					if (tryLeftTag()) {
						if (inmark) {
							System.err
									.println("Skipping last mark" + inputRead);
						}
						startMark = inputPos;
						inmark = true;
					}
					while ((ctr = nextState(currentState, readNext(in))) != null) { //System.out.println("Badam <"+(char)cchar+ "> "+currentState);
						currentState = ctr.state;

						if (tryLeftTag()) {
							if (inmark) {
								System.err.println("Skipping last mark"
										+ inputRead);
							}
							startMark = inputPos;
							inmark = true;
						}
						if (tryRightTag()) {
							if (!inmark) {
								System.err.println("Skipping end mark : "
										+ inputRead);
							} else {
								Marker mark = new Marker();
								mark.end = inputPos;
								mark.start = startMark;
								mark.value = inputRead.substring(startMark,
										inputPos);
								memoryMark.add(mark);
								//System.out.println("Right tag "+inputRead+ ", "+inputPos);
								//System.out.println("Right tag "+mark.value);
								inmark = false;
							}
						}
						if (tryLeftTag()) {
							if (inmark) {
								System.err.println("Skipping last opening mark"
										+ inputRead);
							}
							startMark = inputPos;
							inmark = true;
						}
						eres = this.tryEpsilon();

						if (eres != "") {
							//System.out.println(eres+", "+inputPos);
							lastOutput = outputs;
							cLastPos = inputPos;
							lastEres = eres;
							//match = true;
						}
						if (finalStates.contains(new Integer(currentState))) {
							match = true;
							cLastPos = inputPos;
						}
					}
					eres = this.tryEpsilon();

					if (eres != "") {
						lastOutput = outputs;
						cLastPos = inputPos;
					}
					if (!match) {
						eres = "";
						//outputs = "";
						memoryMark.clear();
						inmark = false;
						if (inputPos > markInputPos) {
							//System.out.println("Reset : markInputPos = "+markInputPos);
							res.startPos = markInputPos;
							inputPos = markInputPos;
							System.out
									.println("Bang bang" + inputRead.length());
							in.unread(inputRead.substring(inputPos)
									.toCharArray());
							//System.out.println("<"+inputRead.substring(inputPos)+"> unread");
							inputRead = inputRead.substring(0, inputPos);
							//System.out.println("End reset: "+inputRead);
							eof = false;
						}
					} else {
						eres = lastEres;
						res.endPos = cLastPos - 1;
						inputPos = cLastPos;
						//System.out.println("Unreading "+inputRead.substring(inputPos)+", "+inputPos); 	
						in.unread(inputRead.substring(inputPos).toCharArray());
						inputRead = inputRead.substring(0, inputPos);
						//System.out.println("Already read "+inputRead); 	
						res.input = inputRead;

						if (eres.matches("^...:.*$")) {
							res.result = evalTodo(eres, inputRead);
							res.label = null;
						} else {
							res.result = inputRead.substring(res.startPos);
							res.label = eres;
						}
						inputPos = markInputPos;
						//System.out.println("<"+res.result+">"); 		   				
						memoryMark.clear();
						inmark = false;
						return res;
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (match) {
			return res;
		} else {
			res.result = null;
			res.eof = eof;
			res.input = inputRead;
			return res;
		}
	}
}